using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

namespace Microsoft.Modeling.StoryboardDesigner
{
    partial class ScenarioShape
    {
        #region Constants that must match values in the Dsl Definition
        private const string TitleFieldName = "Title";
        private const string ExpandCollapseFieldName = "ExpandCollapse";
        private const string BackgroundFieldName = "Background";
        private const string TypeFieldName = "Type";
        #endregion

        // Stores the current collapsed size of the shapes
        private SizeD defaultSize;

        /// <summary>
        /// Override: replaces the Title TextField with an AutoSizeTextField.
        /// </summary>
        /// <remarks>Necessary because the sizing behaviour of TextField isn't
        /// quite what we want: all instances of the shape field are sized based
        /// on whichever shape was last changed. We want the size of the shape field
        /// to vary for each shape it is associated with.</remarks>
        protected override void InitializeShapeFields(IList<ShapeField> shapeFields)
        {
            base.InitializeShapeFields(shapeFields);

            // Now replace the Title TextField with an 
            // AutoSizeTextField.
            TextField titleField = FindShapeField(shapeFields, TitleFieldName) as TextField;
            Debug.Assert(titleField != null, "Error - could not find the Title text field. Has the Dsl Definition changed?");

            AutoSizeTextField newTitleField = CreateAutosizeTextField(TitleFieldName);
            newTitleField.DefaultFontId = new StyleSetResourceId(string.Empty, "ShapeTextBold8");
            shapeFields.Remove(titleField);
            shapeFields.Add(newTitleField);

            // NB must add the shape field after the area shape field, otherwise
            // the title field will be painted behind the area field, and so 
            // won't be visible.
        }

        /// <summary>
        /// Override: changes the anchoring behaviour of the Title field.
        /// </summary>
        /// <remarks>The base class sets the generated anchoring behaviour, which
        /// we then change.</remarks>
        protected override void InitializeDecorators(IList<ShapeField> shapeFields, IList<Decorator> decorators)
        {
            // Decorator initialization sets the anchoring behaviour of 
            // the fields. We let the base class do its stuff, then we
            // tweak the anchoring of the title field.
            base.InitializeDecorators(shapeFields, decorators);

            // Now change the title field anchoring to suit us.
            TextField titleField = FindShapeField(shapeFields, TitleFieldName) as TextField;
            Debug.Assert(titleField != null, "Error - could not find the Title text field. Has the Dsl Definition changed?");

            SetTitleFieldAnchoring(shapeFields, titleField);

            // Anchor the area field to the bottom of the shape.
            ShapeField areaField = FindShapeField(BackgroundGradientFieldName);

            if(areaField == null)
            {
                return;
            }

            areaField.AnchoringBehavior.SetBottomAnchor(AnchoringBehavior.Edge.Bottom, 0.0);
        }

        /// <summary>
        /// Creates and returns a new instance of AutoSizeTextField that 
        /// will display multiline and can be edited.
        /// </summary>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        private static AutoSizeTextField CreateAutosizeTextField(string fieldName)
        {
            AutoSizeTextField field = new AutoSizeTextField(fieldName);

            field.DefaultFocusable = true;
            field.DefaultText = "";
            field.DefaultMultipleLine = true;
            field.DefaultAutoSize = false;

            StringFormat format1 = new StringFormat(StringFormatFlags.NoClip);
            format1.Trimming = StringTrimming.EllipsisWord;
            format1.Alignment = StringAlignment.Near;
            format1.LineAlignment = StringAlignment.Near;

            field.DefaultStringFormat = format1;

            return field;
        }

        /// <summary>
        /// Set the anchoring for the Title text field
        /// </summary>
        /// <remarks>Anchor the top to the top of the parent;
        /// Left to the right of the "Start" decorator;
        /// Right to the left of the expand collapse decorator;
        /// Bottom to the top of the first compartment shape.</remarks>
        private static void SetTitleFieldAnchoring(IList<ShapeField> shapeFields, TextField titleField)
        {
            // Change the anchoring behaviour of the shape
            ShapeField chevronField = ShapeElement.FindShapeField(shapeFields, ExpandCollapseFieldName);
            Debug.Assert(chevronField != null, "Error - could not find GroupExpandCollapse text decorator field - has the DslDefinition changed?");

            ShapeField typeField = ShapeElement.FindShapeField(shapeFields, TypeFieldName);
            Debug.Assert(typeField != null, "Error - could not find Type decorator field - has the DslDefinition changed?");

            titleField.AnchoringBehavior.Clear();
            titleField.AnchoringBehavior.SetTopAnchor(typeField, AnchoringBehavior.Edge.Bottom, 0.0);
            titleField.AnchoringBehavior.SetLeftAnchor(typeField, AnchoringBehavior.Edge.Left, 0.0);
            titleField.AnchoringBehavior.SetRightAnchor(chevronField, AnchoringBehavior.Edge.Left, 0.0);
        }

        /// <summary>
        /// Override to change the generated compartment anchoring behaviour.
        /// </summary>
        /// <remarks>Set the top anchor of the first comparment at a sufficient 
        /// distance to display all of the title text.
        /// Also, store the height required to display the title text, so that we
        /// can collapse the shape down to this size.
        /// Finally, anchor the bottom of the Title text field to the top of the 
        /// first compartment.</remarks>
        public override void AnchorAllCompartments()
        {
            // Let the base class anchor the compartments first.
            base.AnchorAllCompartments();

            Compartment firstComp = GetFirstCompartment();
            if(firstComp == null) { return; }

            TextField titleField = FindShapeField(TitleFieldName) as TextField;
            if(titleField == null) { return; }

            // Change the anchor position of the first compartment
            // so that it is far enough down to display the whole 
            // of the title field.
            double requiredHeight = 
                MultiLineFieldHelper.GetRequiredSizeForFixedWidth(this, titleField).Height + 0.23;
            // Ensure we are never smaller than the base default height
            if(requiredHeight < base.DefaultSize.Height)
            {
                requiredHeight = base.DefaultSize.Height;
            }

            firstComp.Anchoring.SetTopAnchor(AnchoringBehavior.Edge.Top, requiredHeight);

            // Set the default (collapsed) height for the whole shape
            // to be the required height. This means that when 
            // the shape is collapsed, the text will still be visible
            // but the compartments will be hidden.
            defaultSize.Height = requiredHeight;

            // Now tell all of the compartments to re-adjust themselves
            foreach(NodeShape child in this.NestedChildShapes)
            {
                Compartment childComp = child as Compartment;
                if(childComp != null)
                {
                    childComp.PerformShapeAnchoringRule();
                }
            }
        }

        private Compartment GetFirstCompartment()
        {
            foreach(ModelElement child in NestedChildShapes)
            {
                Compartment comp = child as Compartment;
                if(comp != null) { return comp; }
            }
            return null;
        }

        public override SizeD DefaultSize
        {
            get
            {
                if(defaultSize == SizeD.Empty)
                {
                    defaultSize = base.DefaultSize;
                }

                return defaultSize;
            }
        }

        /// <summary>
        /// Method called to tell the shape that a multi-line property
        /// value has changed.
        /// </summary>
        /// <remarks>We need to resize the shape so that all of the text
        /// is visible. Can be done by calling AnchorAllCompartments because
        /// of the modifications we have made to the "DefaultSize" property
        /// implementation.</remarks>
        public void UpdateMultiLineDecoratorSize()
        {
            this.AnchorAllCompartments();
        }
    }
}